package com.wrupple.muba.catalogs.client.module.services.logic.impl;

import java.util.Collection;
import java.util.List;

import com.google.gwt.core.client.JavaScriptObject;
import com.google.gwt.core.client.JsArray;
import com.google.inject.Inject;
import com.wrupple.muba.catalogs.client.module.services.logic.CatalogEntryAssembler;
import com.wrupple.muba.catalogs.client.module.services.logic.CatalogEntryJoiningStrategy;
import com.wrupple.muba.catalogs.domain.CatalogEntry;
import com.wrupple.muba.common.client.JSOHelper;
import com.wrupple.vegetate.client.util.JsArrayList;
import com.wrupple.vegetate.domain.CatalogDescriptor;
import com.wrupple.vegetate.domain.FieldDescriptor;
import com.wrupple.vegetate.domain.JsVegetateResultSet;

public class CatalogEntryJoiningStrategyImpl implements
		CatalogEntryJoiningStrategy {

	private final static class EntryMap extends JavaScriptObject {

		protected EntryMap() {
			super();
		}

		public native JsArray<CatalogEntry> mapMatchingEntries(
				CatalogEntry local, String localField) /*-{
			var valueToLook = local[localField];
			return this[valueToLook];
		}-*/;

	}

	CatalogEntryAssembler assemblyService;

	@Inject
	public CatalogEntryJoiningStrategyImpl(CatalogEntryAssembler assemblyService) {
		super();
		this.assemblyService = assemblyService;
	}

	@Override
	public <T extends JavaScriptObject> List<T> processJoinedEntries(CatalogDescriptor catalog,
			List<CatalogEntry> result) {
		JsVegetateResultSet resultSet;
		int joinIndex = -1;
		JsArray<CatalogEntry> assembledEntries = null;
		JsArray<CatalogEntry> assembledJoinedEntries;
		String foreignCatalog;
		for (CatalogEntry rawResultSet : result) {
			resultSet = rawResultSet.cast();
			foreignCatalog = resultSet.getName();
			assert foreignCatalog != null : "The server returned an unidentified result set, result set name is null";
			if (joinIndex == -1) {
				assembledEntries = assemblyService.processResultSet(resultSet,
						catalog.getId());
			} else {
				assembledJoinedEntries = assemblyService.processResultSet(
						resultSet, foreignCatalog);
				join(assembledEntries, assembledJoinedEntries, catalog,
						foreignCatalog, resultSet.getJoinableField());
			}
			joinIndex++;
		}
		
		JsArray<T> rawReturnValue = assembledEntries.cast();
		List<T> regreso = JsArrayList.arrayAsList(rawReturnValue);
		return regreso;
	}

	private void join(JsArray<CatalogEntry> localEntries,
			JsArray<CatalogEntry> foreignResults, CatalogDescriptor descriptor,
			String joinableCatalog, String foreignJoinableField) {

		Collection<FieldDescriptor> fieldNames = descriptor.getFields();
		String localFieldId = null;
		String localKeyField = descriptor.getKeyField();
		String foreignCatalogId = null;

		for (FieldDescriptor field : fieldNames) {
			foreignCatalogId = field.getForeignCatalog();
			if (foreignCatalogId != null
					&& foreignCatalogId.equals(joinableCatalog)) {
				// this field points to a catalog of the same type as the
				// results
				if (field.isEphemeral() || field.isKey()) {
					// features
					localFieldId = field.getId();
					EntryMap entryMap = buildEntryMap(foreignJoinableField, foreignResults);
					
					if (field.isEphemeral()) {
						// fetch values pointing to this entry from the foreign
						// catalog
						int size = localEntries.length();
						JsArray<CatalogEntry> matches;
						CatalogEntry localEntry;
						String fieldToJoin = field.getId();
						for (int i = 0; i < size; i++) {
							localEntry = localEntries.get(i);
							matches = entryMap.mapMatchingEntries(localEntry,
									localKeyField);
							JSOHelper.setAttribute(localEntry, fieldToJoin,
									matches);
						}
					}
					if (field.isKey()) {

						int size = localEntries.length();
						JsArray<CatalogEntry> matches;
						CatalogEntry localEntry;
						boolean many = field.getWidget() != null && field.getWidget().equals("oneToMany");
						String fieldToJoin = field.getId();
						for (int i = 0; i < size; i++) {
							localEntry = localEntries.get(i);
							matches = entryMap.mapMatchingEntries(localEntry,
									localFieldId);
							if (matches != null && !(matches.length() == 0)) {
								if (many) {
									JSOHelper.setAttribute(localEntry,
											fieldToJoin + "Values", matches);
								} else {
									JSOHelper.setAttribute(localEntry,
											fieldToJoin + "Value", matches.get(0));
								}
							}

						}

					}
				}
			}

		}

	}

	private native EntryMap buildEntryMap(String groupingFielde,
			JsArray<CatalogEntry> foreignEntries) /*-{
		var groupingField = String(groupingFielde);
		var regreso = {};
		var value;
		var array;
		var entry;
		for ( var i = 0 ; i < foreignEntries.length ; i++) {
			entry = foreignEntries[i];
			value = entry[groupingField];
			if (value == null) {
			} else {
				value = String(value);
				array = regreso[value];
				if (array == null) {
					array = new Array();
					regreso[value] = array;
				}
				array.push(entry);
			}

		}
		return regreso;
	}-*/;

}
